###########################################
#
#  ChipWar
#
#  War. War never changes.
#  This game takes place on a toroidal field
#  of 16 territories. Each has some number
#  of troops stationed there (1-5).
#  at the beginning of the game, territories
#  are divided between you (white)
#  and an AI opponent (black).
#
#  You can order your territories to attack
#  adjacent enemy territories provided you
#  have more than one troop available.
#  When you attack, a number of 8-sided dice
#  will be rolled and summed based on the
#  number of attackers and defenders.
#  If the attackers win, they capture the
#  territory and transfer all but one of
#  their troops over.
#  If the defenders win, they destroy all
#  but one of the attackers and lose one troop,
#  or none if they have only a single troop.
#
#  After white has taken a turn,
#  each territory has a 50% chance of
#  producing one new troop.
#  The game ends when black or white has
#  conquered the entire map.
#
#  When selecting a territory, ASWD move
#  your cursor, E selects the territory
#  and Q ends your turn.
#  With a territory selected, ASWD choose
#  the direction in which to attack,
#  E confirms the attack and Q cancels.
#  When the game is over press any key to
#  play again.
#
#  Based loosely on the Flash game DiceWars:
#
#  http://www.gamedesign.jp/flash/dice/dice.html
#
###########################################

: whitedice
	0xFE 0x82 0x82 0x92 0x82 0x82 0xFE 0x00 # 1
	0xFE 0x82 0x8A 0x82 0xA2 0x82 0xFE 0x00 # 2
	0xFE 0x82 0xAA 0x82 0x92 0x82 0xFE 0x00 # 3
	0xFE 0x82 0xAA 0x82 0xAA 0x82 0xFE 0x00 # 4
	0xFE 0x82 0xAA 0x92 0xAA 0x82 0xFE 0x00 # 5

: blackdice
	0xFE 0xFE 0xFE 0xEE 0xFE 0xFE 0xFE 0x00 # 1
	0xFE 0xFE 0xF6 0xFE 0xDE 0xFE 0xFE 0x00 # 2
	0xFE 0xFE 0xD6 0xFE 0xEE 0xFE 0xFE 0x00 # 3
	0xFE 0xFE 0xD6 0xFE 0xD6 0xFE 0xFE 0x00 # 4
	0xFE 0xFE 0xD6 0xEE 0xD6 0xFE 0xFE 0x00 # 5

: arrows
	0x10 0x38 0x7C 0xFE 0x38 0x38 0x38 0x00 # n
	0x10 0x18 0xFC 0xFE 0xFC 0x18 0x10 0x00 # e
	0x38 0x38 0x38 0xFE 0x7C 0x38 0x10 0x00 # s
	0x10 0x30 0x7E 0xFE 0x7E 0x30 0x10 0x00 # w

: p1
	0x00 0x78 0x44 0x44 0x78 0x40 0x40 0x00
	0x10 0x30 0x10 0x10 0x7C 0x00
: p2
	0xFE 0x86 0xBA 0xBA 0x86 0xBE 0xBE 0xFE
	0xC6 0xBA 0xE6 0xDE 0x82 0xFE

: curs1 0xAA 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0xAA
: curs2 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80

: empty 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
: fill  0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
        0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
: plus  0x00 0x40 0xE0 0x40

# game over
: go1 0x7E 0xFE 0xC0 0xDE 0xC6 0xFE 0x7E 0x00 0x7C 0xFE 0xC6 0xC6 0xC6 0xFE 0x7C
: go2 0x7C 0xFE 0xC6 0xFE 0xC6 0xC6 0xC6 0x00 0xC6 0xC6 0xC6 0xC6 0xEE 0x7C 0x38
: go3 0xC6 0xEE 0xFE 0xD6 0xC6 0xC6 0xC6 0x00 0x7E 0xFE 0xC0 0xF8 0xC0 0xFE 0x7E
: go4 0x7E 0xFE 0xC0 0xF8 0xC0 0xFE 0x7E 0x00 0xFC 0xFE 0xC6 0xFE 0xFC 0xCE 0xC6

# you win
: yw1 0xC6 0xC6 0xEE 0x7C 0x38 0x38 0x38 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
: yw2 0x7C 0xFE 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x63 0x63 0x63 0x6B 0x7F 0x77 0x63
: yw3 0xC6 0xC6 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x7E 0x7E 0x18 0x18 0x18 0x7E 0x7E
: yw4 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xC6 0xE6 0xF6 0xFE 0xDE 0xCE 0xC6

# title/loading screen
: ti1 0xFE 0x82 0xAA 0x92 0xAA 0x82 0xFE 0x00 0x06 0x06 0x06 0x06 0x06 0x07 0x03
: ti2 0x00 0x00 0x75 0x47 0x45 0x75 0x00 0x00 0x19 0x1B 0xDB 0xDB 0xDB 0xFB 0x33 
: ti3 0x00 0x00 0x5C 0x54 0x5C 0x50 0x00 0x00 0xE7 0xF7 0x36 0xF7 0xF7 0x36 0x36 
: ti4 0xFE 0xFE 0xF6 0xFE 0xDE 0xFE 0xFE 0x00 0xC0 0xE0 0x60 0xC0 0xE0 0x60 0x60
: lo1 0x8E 0x8A 0x8A 0xEE
: lo2 0x4C 0xAA 0xEA 0xAC
: lo3 0xB3 0xAA 0xAA 0xAB
: lo4 0x80 0x00 0x80 0xAA

#           n  e  s  w
: delta-x   0  1  0 -1
: delta-y  -1  0  1  0

###########################################
#
#  Register Map:
#
###########################################

# vf is always temporary
:alias mode         ve
:alias cursorx      vd
:alias cursory      vc
:alias dir          vb
:alias compare-temp va
:alias wdice        v9
:alias bdice        v8
:alias wtotal       v7
:alias btotal       v6
:alias wroll        v5
:alias broll        v4
:alias rolls        v3
# v0-v2 are temporary

###########################################
#
#  Mutable Data
#
###########################################

# game modes (mode)
:const MOVE    0
:const ATTACK  1
:const AIMOVE  2
:const WIN     3
:const LOSE    4
:const RESET   5

# space owners (owns-at)
:const START 0
:const WHITE 1
:const BLACK 2
:const EMPTY 3

: bcd-buffer 0
: tens       0
: ones       0

: owns-at  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
: dice-at  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

###########################################
#
#  Utility Subroutines
#
###########################################

: random-vector
	# trashes v0-v2
	# returns in v1
	v1 := random 0xFF
	v2 := v1
	v0 := 0
	loop
		while v2 != 0
		v2 <<= v2
		v0 += vf
	again
	if v0 != 4 then jump random-vector
;

: vfwait
	delay := vf
	loop
		vf := delay
		if vf != 0 then
	again
;

: longwait
	vf := 60
	jump vfwait

: wait
	vf := 30
	jump vfwait

: uwait
	vf := 10
	jump vfwait

: fillscreen
	# trashes v0-v1
	clear
	i := fill
	v0 := 0
	loop
		v1 := 0
		sprite v0 v1 15
		v1 := 15
		sprite v0 v1 15
		v1 := 30
		sprite v0 v1 2
		v0 += 8
		if v0 != 64 then
	again
;

: draw-end-screen
	v0 := 16
	v1 :=  8
	v2 := 15
	loop
		sprite v0 v1 15
		i += v2
		v0 += 8
		if v0 != 48 then
	again
	v0 := key
	mode := RESET
;

: draw-game-over
	fillscreen
	i := go1
	jump draw-end-screen

: draw-you-win
	clear
	i := yw1
	jump draw-end-screen

###########################################
#
#  Battle
#
###########################################

: draw-wtotal
	# trashes v0-v2
	v2 := 23
	v1 := 11
	i := bcd-buffer
	bcd wtotal
	i := tens load v0 i := hex v0
	sprite v1 v2 5
	v1 += 5
	i := ones load v0 i := hex v0
	sprite v1 v2 5
;

: draw-btotal
	# trashes v0-v2
	v2 := 23
	v1 := 43
	i := bcd-buffer
	bcd btotal
	i := tens load v0 i := hex v0
	sprite v1 v2 5
	v1 += 5
	i := ones load v0 i := hex v0
	sprite v1 v2 5
;

: draw-wroll
	# trashes v0-v2
	v2 := 16
	v1 := 14
	i := hex wroll
	sprite v1 v2 5
	v1 += -4
	i := plus
	sprite v1 v2 4
;

: draw-broll
	# trashes v0-v2
	v2 := 16
	v1 := 46
	i := hex broll
	sprite v1 v2 5
	v1 += -4
	i := plus
	sprite v1 v2 4
;

: draw-battle
	# trashes v0-v2
	clear
	i  := fill
	v0 := 32
	v1 := 0
	loop
		v1 := 0
		sprite v0 v1 15
		v1 := 15
		sprite v0 v1 15
		v1 := 30
		sprite v0 v1  2
		v0 += 8
		if v0 != 64 then
	again
	

	# dice counters:
	v1 :=  4
	v0 := 12

	i  := whitedice
	v2 := wdice
	v2 <<= v2
	v2 <<= v2
	v2 <<= v2
	v2 += -8
	i  += v2
	sprite v0 v1 7

	i  := whitedice
	v2 := bdice
	v2 <<= v2
	v2 <<= v2
	v2 <<= v2
	v2 += -8
	i  += v2
	v0 := 45
	sprite v0 v1 7

	draw-wtotal
	draw-btotal
;

: b-do-roll
	broll := random 7
	broll += 1
	draw-broll
	draw-btotal
	btotal += broll
	draw-btotal
;

: w-do-roll
	wroll := random 7
	wroll += 1
	draw-wroll
	draw-wtotal
	wtotal += wroll
	draw-wtotal
;

: defender-wins
	# set attacker to 1
	cursor-to-index
	i := dice-at
	i += vf
	v0 := 1
	save v0

	# set defender to max(1, old-1)
	direction-to-index
	i := dice-at
	i += vf
	load v0
	v0 += -1
	if v0 == 0 then v0 := 1
	i := dice-at
	i += vf
	save v0
;

: attacker-wins
	# set dest to attackers - 1
	cursor-to-index
	i := dice-at
	i += vf
	load v0
	va := v0
	va += -1
	direction-to-index
	i := dice-at
	i += vf
	v0 := va
	save v0

	# set dest to be attacker owned
	direction-to-index
	i := owns-at
	i += vf
	if mode == ATTACK then v0 := WHITE
	if mode != ATTACK then v0 := BLACK
	save v0

	# set attackers to 1
	cursor-to-index
	i := dice-at
	i += vf
	v0 := 1
	save v0
;

: white-wins-attack
	attacker-wins

	# if there are no black zones,
	# game over and white wins.
	i := owns-at
	v1 := 0
	loop
		load v0
		if v0 == BLACK then return
		v1 += 1
		if v1 != 18 then
	again
	mode := WIN
;

: black-wins-attack
	attacker-wins

	# if there are no white zones,
	# game over and black wins.
	i := owns-at
	v1 := 0
	loop
		load v0
		if v0 == WHITE then return
		v1 += 1
		if v1 != 18 then
	again
	mode := LOSE
;

: white-wins
	if mode == ATTACK then jump white-wins-attack
	jump defender-wins

: black-wins
	if mode != ATTACK then jump black-wins-attack
	jump defender-wins

: battle
	wtotal := 0
	btotal := 0
	rolls  := 0

	draw-battle
	wait

	loop
		if bdice > rolls then b-do-roll
		if wdice > rolls then w-do-roll
		wait
		if bdice > rolls then draw-broll
		if wdice > rolls then draw-wroll
		uwait

		rolls += 1

		# get max of dice count
		v0 := bdice
		if wdice > bdice then v0 := wdice

		if rolls != v0 then
	again
	longwait

	# note that this gives black a fundamental
	# advantage; it always wins ties:
	if wtotal > btotal then jump white-wins
	jump black-wins

###########################################
#
#  Map
#
###########################################

: map-tile
	# trashes v0-v2.
	# takes an index in vf
	# initializes i to the appropriate
	# sprite data to draw

	v2 := vf

	i := dice-at
	i += v2
	load v0
	v0 += -1
	v1 <<= v0
	v1 <<= v1
	v1 <<= v1

	i := owns-at
	i += v2
	load v0

	i := whitedice
	if v0 == BLACK then i := blackdice
	i += v1
	if v0 == EMPTY then i := empty
;

: draw-map
	# trashes v0-v5
	v4 := 4 # y position
	v5 := 0 # map array index

	clear
	loop
		v3 := 8 # x position
		loop
			vf := v5 map-tile
			sprite v3 v4 7

			v5 += 1
			v3 += 8

			if v3 != 56 then
		again
		v4 += 8
		if v4 != 28 then
	again
;

: draw-p1
	i  := p1
	v0 := 0
	v1 := 9
	sprite v0 v1 14
;

: draw-p2
	i  := p2
	v0 := 56
	v1 := 9
	sprite v0 v1 14
;

###########################################
#
#  Cursor
#
###########################################

: draw-cursor
	# trash v0-v2
	v0 <<= cursorx
	v0 <<= v0
	v0 <<= v0
	v0 += 7

	v1 <<= cursory
	v1 <<= v1
	v1 <<= v1
	v1 += 3

	v2 := v0
	v2 += 8
	i  := curs1
	sprite v0 v1 9
	i  := curs2
	sprite v2 v1 9
;

: cursor-to-index
	# leave result in vf
	vf := cursorx
	if cursory == 1 then vf +=  6
	if cursory == 2 then vf += 12
;

: direction-to-index
	# trashes v0-v2
	# leave the result in vf
	v1 := cursorx
	i := delta-x
	i += dir
	load v0
	v1 += v0
	if v1 == -1 then v1 := 5
	if v1 ==  6 then v1 := 0

	v2 := cursory
	i := delta-y
	i += dir
	load v0
	v2 += v0
	if v2 == -1 then v2 := 2
	if v2 ==  3 then v2 := 0

	if v2 == 1 then v1 += 6
	if v2 == 2 then v1 += 12
	vf := v1
;

: cursor-to-xy
	# leave result in v0/v1
	v0 <<= cursorx
	v0 <<= v0
	v0 <<= v0
	v0 += 8

	v1 <<= cursory
	v1 <<= v1
	v1 <<= v1
	v1 += 4	
;

: erase-selected
	# trash v0-v2
	cursor-to-index
	map-tile
	cursor-to-xy
	sprite v0 v1 7
;

: draw-arrow
	i := arrows
	v0 <<= dir
	v0 <<= v0
	v0 <<= v0
	i += v0
	cursor-to-xy
	sprite v0 v1 7
;

: start-attack
	dir := 0

	# ignore spaces we don't control
	cursor-to-index
	i := owns-at
	i += vf
	load v0
	if v0 != WHITE then return

	# ignore spaces with 1 die
	i := dice-at
	i += vf
	load v0
	if v0 == 1 then return

	# switch to attack selection mode
	mode := ATTACK
	draw-cursor
	erase-selected
	draw-arrow
;

: cancel-attack
	draw-arrow
	mode := MOVE
	draw-cursor
	erase-selected
;

: start-fight
	direction-to-index

	# if the adjacent tile isn't black, give up
	i := owns-at
	i += vf
	load v0
	if v0 != BLACK then return

	# set up and fight
	i := dice-at
	i += v1
	load v0
	bdice := v0

	i := dice-at
	cursor-to-index
	i += vf
	load v0
	wdice := v0

	battle
	if mode == WIN then return
	draw-map
	draw-p1
	draw-cursor
	mode := MOVE
;

: start-ai-turn
	restock
	draw-map
	longwait
	draw-p2
	mode := AIMOVE
	ai-move
	if mode == LOSE then return
	mode := MOVE
	draw-p2
	draw-p1
;

: move-cursor
	va := key
	draw-cursor
	if va == 7 then cursorx += -1
	if va == 9 then cursorx +=  1
	if va == 5 then cursory += -1
	if va == 8 then cursory +=  1
	if va == 6 then start-attack
	if va == 4 then start-ai-turn

	if cursorx ==  6 then cursorx := 0
	if cursorx == -1 then cursorx := 5
	if cursory ==  3 then cursory := 0
	if cursory == -1 then cursory := 2
	draw-cursor
;

: attack-cursor
	va := key
	draw-cursor
	draw-arrow
	if va == 7 then dir := 3
	if va == 9 then dir := 1
	if va == 5 then dir := 0
	if va == 8 then dir := 2
	draw-arrow
	draw-cursor
	if va == 4 then cancel-attack
	if va == 6 then start-fight
;

###########################################
#
#  AI Opponent
#
###########################################

: ai-pickdir
	direction-to-index

	# must be white
	i := owns-at
	i += vf
	load v0
	if v0 != WHITE then return

	# is this zone weaker?
	i := dice-at
	i += vf
	load v0
	if v5 < v0 then return

	# our new choice
	v4 := dir
	v5 := v0
;

: ai-single
	# pick a random territory
	v2 := random 0b1111
	v1 := 0
	cursorx := 0
	cursory := 0
	i := owns-at
	loop
		while v2 != 0
		load v0
		if v0 != EMPTY then v2 += -1
		v1 += 1
		cursorx += 1
		if cursorx == 6 then cursory += 1
		if cursorx == 6 then cursorx := 0
	again
	# v1 now contains the (0-17) index of
	# our chosen territory.

	# if it's not black, ignore it
	i := owns-at
	i += v1
	load v0
	if v0 != BLACK then return

	# if it has 1 die, ignore it
	i := dice-at
	i += v1
	load v0
	if v0 == 1 then return
	bdice := v0

	# attack the weakest adjacent white zone
	v4 := -1   # best direction
	v5 := 0xFF # best die count (lower is better)
	dir := 0 ai-pickdir
	dir := 1 ai-pickdir
	dir := 2 ai-pickdir
	dir := 3 ai-pickdir
	if v4 == -1 then return
	dir   := v4
	wdice := v5

	# show arrow
	draw-cursor
	longwait
	draw-cursor
	erase-selected
	draw-arrow
	longwait

	# FIRE ZE MISSILES
	battle
	draw-map
	draw-p2
	wait
;

# unroll these loops so that I don't
# need to keep track of a loop index:

: ai-moves
	ai-single
	ai-single
	ai-single
	ai-single
	ai-single
;

: ai-move
	ai-moves
	ai-moves
	ai-moves
	ai-moves
;

###########################################
#
#  Restocking
#
###########################################

: restock-single
	# take index in va
	i  := owns-at
	i  += va
	load v0
	if v0 == EMPTY then return

	vb += 1
	i  := dice-at
	i  += va
	load v0
	v1 <<= v1
	v0 += vf
	if v0 == 6 then v0 := 5

	i  := dice-at
	i  += va
	save v0
;

: restock
	va := 0 # cell index
	vb := 0 # bit  index
	random-vector
	loop
		restock-single
		va += 1
		if vb != 8 then
	again
	random-vector
	loop
		restock-single
		va += 1
		if vb != 16 then
	again
;

###########################################
#
#  Map Generation
#
###########################################

: fill-boards
	# expects i to be initialized
	# takes the fill value in v0
	v1 := 0
	loop
		save v0
		v1 += 1
		if v1 != 18 then
	again
;

: reset-owned
	i  := owns-at
	v0 := START
	jump fill-boards

: reset-dice
	i  := dice-at
	v0 := 1
	jump fill-boards

: place-empty
	# trashes v0-v2
	# pick an index 0-17
	v2 := random 0b11111
	if v2 > 17 then jump place-empty
	
	# make sure that spot is uninitialized
	i := owns-at
	i += v2
	load v0
	if v0 != START then jump place-empty

	# set it to be empty
	i := owns-at
	i += v2
	v0 := EMPTY
	save v0
;

: place-owned-single
	# take index in va
	i  := owns-at
	i  += va
	load v0
	if v0 == EMPTY then return

	v1 <<= v1
	vb += 1
	v0 := WHITE
	if vf == 1 then v0 := BLACK
	i  := owns-at
	i  += va
	save v0
;

: place-owned
	va := 0 # cell index
	vb := 0 # bit  index
	random-vector
	loop
		place-owned-single
		va += 1
		if vb != 8 then
	again
	random-vector
	loop
		place-owned-single
		va += 1
		if vb != 16 then
	again
;

: random-map
	reset-owned
	reset-dice
	place-empty
	place-empty
	place-owned
	restock
;

###########################################
#
#  Main
#
###########################################

: title-screen
	v0 := 16
	v1 :=  4
	v2 :=  0
	v3 := 15
	i  := ti1
	loop
		sprite v0 v1 15
		v0 +=  8
		v2 +=  1
		i  += v3
		if v2 != 4 then
	again

	v0  := 16
	v1  := 27
	v2  :=  0
	v3  :=  4
	loop
		sprite v0 v1 4
		v0 += 8
		v2 += 1
		i  += v3
		if v2 != 4 then
	again
;

: init-game
	random-map
	draw-map
	draw-p1
	draw-cursor
	mode := MOVE
;

: main
	title-screen
	init-game
	loop
		if mode == MOVE    then move-cursor
		if mode == ATTACK  then attack-cursor
		if mode == WIN     then draw-you-win
		if mode == LOSE    then draw-game-over
		if mode == RESET   then init-game
	again
